import os, sys, inspect

# Import modules to be processed.
from masdevallia.modeling import Curves
from masdevallia.rigging import Controls, CurveControl, PolygonRigControl
from masdevallia.rigging import RigJoint, RigModule, RigModuleArm, SurfaceRigControl
from masdevallia.settings import PathConstants
from masdevallia.utility import Colors, Dag, FileSystem, Matrix, MScriptUtility, Node
from masdevallia.utility import RigControls, Selection, Vector, WindowLauncher, WindowUtility
from masdevallia.utility import XML
from masdevallia.windows import AnimationLibrary, BuildModules, BuildRigs, CharacterMap
from masdevallia.windows import Dialogs, InfoTile, RigLibrary, RigTools

MODULES = {'modeling.Curves':[Curves]}

'''
MODULES = {'modeling.Curves':[Curves],
           'rigging.Controls':[Controls],
           'rigging.CurveControl':[CurveControl],
           'rigging.PolygonRigControl':[PolygonRigControl],
           'rigging.RigJoint':[RigJoint],
           'rigging.RigModule':[RigModule],
           'rigging.RigModuleArm':[RigModuleArm],
           'rigging.SurfaceRigControl':[SurfaceRigControl],
           'settings.PathConstants':[PathConstants],
           'utility.Colors':[Colors],
           'utility.Dag':[Dag],
           'utility.FileSystem':[FileSystem],
           'utility.Matrix':[Matrix],
           'utility.MScriptUtility':[MScriptUtility],
           'nodes.Node':[Node],
           'utility.RigControls':[RigControls],
           'utility.Selection':[Selection],
           'utility.Vector':[Vector],
           'utility.WindowLauncher':[WindowLauncher],
           'utility.WindowUtility':[WindowUtility],
           'utility.XML':[XML],
           'windows.AnimationLibrary':[AnimationLibrary],
           'windows.BuildModules':[BuildModules],
           'windows.BuildRigs':[BuildRigs],
           'windows.CharacterMap':[CharacterMap],
           'windows.Dialogs':[Dialogs],
           'windows.InfoTile':[InfoTile],
           'windows.RigLibrary':[RigLibrary],
           'windows.RigTools':[RigTools]}
'''

def createPredefs( inPackageName, inModules ):
    '''
    Loops through the passed in modules and creates pypredef files from them.
    
    @type inPackageName: String.
    @type inModules: List. Imported modules from MODULES
    '''
    if not os.path.exists( PathConstants.PATH_MASDEVALLIA_PYPREDEF ):
        
        os.makedirs( PathConstants.PATH_MASDEVALLIA_PYPREDEF )
        
    pypredef_file = open( '{0}\\masdevallia.{1}.pypredef'.format( PathConstants.PATH_MASDEVALLIA_PYPREDEF, inPackageName ), 'w+' )
 
    for module in inModules:
        
        stub_header = createStubHeader( module )
        pypredef_file.write( stub_header )
       
        for name in dir( module ):
           
            try:
               
                exec( 'from masdevallia.{0} import {1}'.format( inPackageName, name ) )
                name_eval = eval( name )

                print 'Building {0}...'.format( name )
               
                makeIt( name_eval, name, pypredef_file )
           
            except Exception, e:
               
                exc_type, exc_obj, exc_tb = sys.exc_info()
                func_name = os.path.split( exc_tb.tb_frame.f_code.co_filename )[1]
                print 'ERROR line {0} in {1} of type {2}'.format( exc_tb.tb_lineno, func_name, exc_type )
                print '    {0}'.format( exc_obj )
                
                
                #print 'Error in createPredefs( {0}, {1} )\n    {2}'.format( inPackageName, inModules, e )
                continue
   
    pypredef_file.close()   

def createStubHeader( inModule ):
    '''
    Creates string containing information about the module. Uses as a header
    for the pypredef file.
   
    @type inModule: Module.
    @return: String
    '''
    stub = "'''\n"    
    stub += 'Stub for module: {0}\n'.format( inModule.__name__ )
    stub += 'Source file: {0}\n'.format( inModule.__file__ )
    stub += inModule.__doc__    
    stub += "'''\n\n\n"   
    return stub

def getArgs( inFunc ):
    '''
    Gets all the arguments for the pass in function.
   
    @type inFunc: Function.
    @return: List.
    '''
    args_final = []
    
    try:
        print 2
        args, varargs, kwargs, defaults = inspect.getargspec( inFunc )

        print args, varargs, kwargs, defaults
       
        # Strip the self arg if it's present.
        self_flag = False
        print 4
        if args:
            
            if args[0] == 'self':
            
                args.remove( 'self' )
                self_flag = True
           
            print 5
            # Match up the arguments with their defaults (if there are any).
            for idx, arg in enumerate( args ):
                
                print idx, arg
               
                if defaults:
                   
                    args_final.append( [arg, defaults[idx]] )
                   
                else:
                   
                    args_final.append( [arg, 'NO DEFAULT'] )
         
            print 6
            # Add the self flag back.
            if self_flag:
               
                args_final.insert( 0, ['self', 'NO DEFAULT'] )
                
    except Exception, e:
   
        exc_type, exc_obj, exc_tb = sys.exc_info()
        func_name = os.path.split( exc_tb.tb_frame.f_code.co_filename )[1]
        print 'ERROR line {0} in {1} of type {2}'.format( exc_tb.tb_lineno, func_name, exc_type )
        print '    {0}'.format( exc_obj )
       
    return args_final
   
def makeIt( inEval, inScopeName, inPypredefFile, noclasses=False, indent='' ):
    '''
    Does the bulk of the work of analyzing PY files to find the correct data to
    be written out to pypredefs.
    
    @type inEval: Python Object. Could be String, Int, Float, Class, Function.
    @type inScopeName: String or Method. Name of the module or Method from a Class.
    @type inPypredefFile: Opened system file.
    @type noclasses: Bool. Whether or not to ignore making class entries.
    @type indent: String. Amount of indent to apply to the written out strings.
    '''
    try:
        if isinstance( inScopeName, str ) and inScopeName.startswith( '__' ):
            return
       
        elif isinstance( inEval, int ):
            
            inPypredefFile.write( '{0}{1} = int\n'.format( indent, inScopeName ) )
            return
           
        elif isinstance( inEval, float ):
            
            inPypredefFile.write( '{0}{1} = float\n'.format( indent, inScopeName ) )
            return
     
        elif isinstance( inEval, str ):
            
            inPypredefFile.write( '{0}{1} = str\n'.format( indent, inScopeName ) )
            return
       
        elif inspect.isclass( inEval ):
           
            if noclasses:
               
                return
           
            try:
                           
                inPypredefFile.write( '{0}class {1}:\n'.format( indent, inScopeName ) )
           
            except:
                           
                inPypredefFile.write( '{0}class {1}:\n'.format( indent, inScopeName.__name__ ) )
                
            if inEval.__doc__:
                
                line = "    '''"
                line += inEval.__doc__
                line += "'''\n"
                inPypredefFile.write( line )
                
            members = inspect.getmembers( inEval, predicate=inspect.ismethod )
            
            print '    members\n        {0}'.format( members )
            
            for name, value in members:
               
                attr = getattr( inEval, name )                        
                makeIt( attr, value, inPypredefFile, noclasses=True, indent='{0}    '.format( indent ) )
       
        elif callable( inEval ):
           
            if hasattr( inEval, '__objclass__' ):
                   
                inPypredefFile.write( '{0}def {1}(self):\n{2}    pass\n\n'.format( indent, inScopeName.__name__, indent ) )
           
            else:
                
                try:
                    
                    line = '{0}def {1}('.format( indent, inScopeName.__name__ )
                    
                except:
                    
                    line = '{0}def {1}('.format( indent, inScopeName )
               
                args = getArgs( inEval )           
                
                iter = 1
                
                print '    args:\n        {0}'.format( args )
                
                for arg in args:
                   
                    if iter == len( args ):
                       
                        if arg[1] == 'NO DEFAULT':
                           
                            line += ' {0} )'.format( arg[0] )
                           
                        else:
                           
                            line += ' {0}={1} )'.format( arg[0], arg[1] )
                           
                    else:
                       
                        if arg[1] == 'NO DEFAULT':
                       
                            line += ' {0},'.format( arg[0] )
                       
                        else:
                   
                            line += ' {0}={1},'.format( arg[0], arg[1] )
                   
                    iter += 1
                
                if inEval.__doc__:
                    
                    line += ':\n'
                    line += "{0}    '''{1}'''\n".format( indent, inEval.__doc__ )
                    line += '{0}    pass\n\n'.format( indent )
                    inPypredefFile.write( line )
               
            return
       
        return
    except Exception, e:
               
        exc_type, exc_obj, exc_tb = sys.exc_info()
        func_name = os.path.split( exc_tb.tb_frame.f_code.co_filename )[1]
        print 'ERROR line {0} in {1} of type {2}'.format( exc_tb.tb_lineno, func_name, exc_type )
        print '    {0}'.format( exc_obj )
       
for name, module in MODULES.items():
    createPredefs( name, module )